package com.lagou.edu.ioc;

import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Transactional;
import com.lagou.edu.utils.PackagingScanUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Kirito
 * @description:
 * @date 2020/04/04
 */
public class ClassPathXmlBeanFactory {

    private ClassPathXmlBeanFactory() {
    }

    private static ClassPathXmlBeanFactory beanFactory = new ClassPathXmlBeanFactory();

    public static ClassPathXmlBeanFactory getInstance() {
        return beanFactory;
    }

    private Map<String, Object> containers;

    private List<BeanDefinition> beanDefinitions;

    private String baseLocation;

    public <T> T getBean(Class<T> tClass) {
        String simpleName = tClass.getSimpleName();
        char[] chars = simpleName.toCharArray();
        chars[0] += 32;
        simpleName = String.valueOf(chars);
        return (T) containers.get(simpleName);
    }

    public Object getBean(String id) {
        return containers.get(id);
    }

    public void doLoadBean(String location) throws Exception {
        // 初始化工厂属性，解析beans.xml文件路径
        prepare(location);

        // 解析beans.xml，读取Bean信息并封装到BeanDefinition
        loadBeanDefinitions();

        // 初始化Bean
        beanInitialization();

        // 填充bean属性，进行依赖注入
        populateBean();

        // 为加了事务控制的类生成代理对象
        afterBeanCreated();
    }

    /**
     * 初始化工厂属性，解析beans.xml文件路径
     *
     * @param location String
     */
    private void prepare(String location) {
        baseLocation = location;
        if (baseLocation.startsWith("classpath")) {
            String[] split = location.split(":");
            baseLocation = split[split.length - 1];
        }

        containers = new ConcurrentHashMap<>();
        beanDefinitions = new ArrayList<>();
    }

    /**
     * 解析beans.xml，读取Bean信息并封装到BeanDefinition
     *
     * @throws Exception Exception
     */
    private void loadBeanDefinitions() throws Exception {
        InputStream resource = Thread.currentThread().getContextClassLoader().getResourceAsStream(baseLocation);
        Document document = new SAXReader().read(resource);
        Element rootElement = document.getRootElement();
        List<Element> elements = rootElement.selectNodes("//scan-packaging");

        PackagingScanUtils packagingScanUtils = new PackagingScanUtils();
        for (Element element : elements) {
            String packageName = element.attributeValue("value");
            beanDefinitions.addAll(packagingScanUtils.scanPackage(packageName));
        }
    }

    /**
     * 反射初始化Bean
     *
     * @throws IllegalAccessException IllegalAccessException
     * @throws InstantiationException InstantiationException
     */
    private void beanInitialization() throws IllegalAccessException, InstantiationException {
        for (BeanDefinition beanDefinition : beanDefinitions) {
            Object obj = beanDefinition.getClazz().newInstance();
            containers.put(beanDefinition.getId(), obj);
        }
    }

    /**
     * 填充bean属性，进行依赖注入
     */
    private void populateBean() throws IllegalAccessException {
        for (Map.Entry<String, Object> entry : containers.entrySet()) {
            Object object = entry.getValue();
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                Autowired annotation = field.getAnnotation(Autowired.class);
                if (null == annotation) {
                    continue;
                }
                // 获取字段名
                String name = field.getName();
                field.setAccessible(true);
                // 如果是直接引用类型 private ConnectionUtils connectionUtils; 字段名就对应id
                Object reference = containers.get(name);
                // 获取加载的BeanDefinition中包含该字段类型的BeanDefinition
                Optional<BeanDefinition> beanDefinitionOptional = beanDefinitions.stream()
                        .filter(beanDefinition -> beanDefinition.getInterfaces().contains(field.getType())).findFirst();
                if (null != reference) {
                    // 如果是直接引用类型 private ConnectionUtils connectionUtils; 根据字段类型直接去容器获取指定对象
                    field.set(object, reference);
                } else if (beanDefinitionOptional.isPresent()) {
                    // 如果是接口类型 private IAccountDao accountDao; 获取接口对应的实现的bean id
                    String id = beanDefinitionOptional.get().getId();
                    reference = containers.get(id);
                    field.set(object, reference);
                } else {
                    continue;
                }

                // 将属性注入后的object重新添加到集合
                containers.put(entry.getKey(), object);
            }
        }
    }

    /**
     * 为加了事务控制的类生成代理对象
     */
    public void afterBeanCreated() {
        ProxyFactory proxyFactory = getBean(ProxyFactory.class);
        for (Map.Entry<String, Object> entry : containers.entrySet()) {
            Object proxy;
            Object bean = entry.getValue();
            Class<?> clazz = bean.getClass();
            Transactional annotation = clazz.getAnnotation(Transactional.class);
            if (null != annotation) {

                Class<?>[] interfaces = clazz.getInterfaces();
                if (interfaces.length == 0) {
                    proxy = proxyFactory.createCglibProxy(bean);
                } else {
                    proxy = proxyFactory.createJdkProxy(bean);
                }
                containers.put(entry.getKey(), proxy);
            }
        }
    }
}
